/****************************************************************************
 * Chariott - Continuous Integration Testing Tool
 * Zaid Mohsin
 * The University of York
 * 2012/2013
 * MSc in Information Technology
 * All rights reserved.
 ****************************************************************************/
package chariott.main;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.perf4j.LoggingStopWatch;
import org.perf4j.StopWatch;

import chariott.configuration.ConfigResolver;
import chariott.configuration.Globals;
import chariott.configuration.XMLTestResolver;
import chariott.partition.AlgorithmType;
import chariott.testing.AntScriptGenerator;
import chariott.testing.ScriptGenerator;

/**
 * ChariottLauncher is the main class for starting the ChARRIOTT tool it uses
 * the observer pattern to detect running cloud nodes
 * 
 * @author Zaid
 * 
 */
public class ChariottLauncher implements PropertyChangeListener {

	private ConfigResolver configResolver;
	private XMLTestResolver testResolver;
	private ScriptGenerator myScriptGenerator;
	private CloudConnectable myCloudClient;
	private SSHConnector mySSH;
	private ArrayList<File> scriptFiles;
	private int numOfNodes;
	private StopWatch stopWatch;
	private ExecutorService executor;

	public ChariottLauncher() {

		// fetch the configurations
		configure();
		// run the directives
		init();
	}

	/**
	 * initializes the cloud nodes, creates an SSH entity, generates the scripts
	 * and Logs the information
	 * 
	 */
	private void init() {
		// create an executor to send/execute scripts to cloud nodes
		// concurrently
		executor = Executors.newFixedThreadPool(Integer
				.parseInt(Globals.AWS_NO_OF_INSTANCES));

		// generate the ant scripts
		myScriptGenerator = new AntScriptGenerator(numOfNodes,
				testResolver.fetchRunItems());

		scriptFiles = myScriptGenerator.generate();

		// start the stop watch to measure cloud overhead performance
		stopWatch = new LoggingStopWatch();

		// logger.info(Globals.AWS_ENDPOINT);
		Main.logger.info(Globals.AWS_AMI);
		Main.logger.info(Globals.AWS_INSTANCE_TYPE);
		Main.logger.info(Globals.AWS_KEY_NAME);
		Main.logger.info(Globals.AWS_SECURITY_GROUP);
		Main.logger.info(Globals.SSH_PEM_FILE);
		Main.logger.info(Globals.SSH_SESSION_USER);
		Main.logger.info(Globals.SVN_REPOSITORY);
		Main.logger.info(Globals.TESTING_PROJECT_NAME);
		Main.logger.info(Globals.TESTING_TEST_FOLDER);

		// create a new cloud client
		myCloudClient = new AWSProvider();
		// register this class to observer the cloud client
		myCloudClient.addChangeListener(this);

		mySSH = new SSHConnector();

		// connect to the cloud service
		myCloudClient.clientConnect();
		// run the cloud nodes needed as number of scripts generated by the
		// ScriptGenerator
		myCloudClient.runInstances(numOfNodes);

		executor.shutdown();
		// wait for all threads to shutdown
		while (!executor.isTerminated()) {

		}

		Main.logger.info("Finshed all threads");
	}

	/**
	 * calls the configurations for parsing the items to test and system
	 * configurations
	 * 
	 */
	private void configure() {
		// parse the configurations from the configuration file
		try {
			Main.logger.info("Resolving the Configurations File");
			configResolver = new ConfigResolver(new File("chariottconfig.ini"));
			testResolver = new XMLTestResolver(new File(
					Globals.TESTING_CONFIG_FILE));
		} catch (IOException e) {
			// TODO Auto-generated catch block

			e.printStackTrace();
		}

		// set the number of cloud nodes to run
		numOfNodes = Integer.parseInt(Globals.AWS_NO_OF_INSTANCES);

		// parse the test orderings for Chariott
		Main.logger.info("Resolving the Tests File");
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {

		if (evt.getNewValue().equals("running")) {

			// get the hostname to send the scriptfile to
			final String hostname = evt.getPropertyName();
			// get the node Index
			final int nodeIndex = (Integer) evt.getSource();
			// concurrently send and execute the script file.
			executor.execute(new Runnable() {
				public void run() {
					sshExecute(hostname, scriptFiles.get(nodeIndex), nodeIndex);
				}
			});

		}

	}

	/**
	 * method that passes a single scriptfile to a cloud name through its
	 * hostname and execute it
	 * 
	 * @param hostName
	 *            the IP or hostname of the cloud node
	 * @param scriptFile
	 *            the script file to be executed
	 */
	private void sshExecute(String hostName, File scriptFile, int nodeIndex) {
		// wait for the Operating System to Load before SSH into it

		try {
			Thread.sleep(45000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		mySSH.executeSequence(hostName, scriptFile);

		Main.logger.info(stopWatch.stop("Node #" + nodeIndex));

	}

}
